﻿//	Copyright (c) 2012 Jonathan Loe
//
//	MIT license (http://en.wikipedia.org/wiki/MIT_License)
//
//	Permission is hereby granted, free of charge, to any person obtaining a copy
//	of this software and associated documentation files (the "Software"), to deal
//	in the Software without restriction, including without limitation the rights 
//	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
//	of the Software, and to permit persons to whom the Software is furnished to do so, 
//	subject to the following conditions:
//
//	The above copyright notice and this permission notice shall be included in all 
//	copies or substantial portions of the Software.
//
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
//	INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
//	PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
//	FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
//	ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;
using System.Text;

using Microsoft.SqlServer.Management.Smo;

using Ordinaire.Security.Data;
using Ordinaire.Social.Data;
using Ordinaire.Taxonomy.Data;

namespace Ordinaire.Db
{
    /// <summary>
    /// OContext class derives DataContext class that provides properties to data table.
    /// </summary>
    public class OContext : DataContext
    {
        #region Tables

        /// <summary>
        /// Table "Roles".
        /// </summary>
        public Table<Roles> Roles;

        /// <summary>
        /// Table "Users".
        /// </summary>
        public Table<Users> Users;

        /// <summary>
        /// Table "UserRoles".
        /// </summary>
        public Table<UserRoles> UserRoles;

        /// <summary>
        /// Table "Addresses".
        /// </summary>
        public Table<Addresses> Addresses;

        /// <summary>
        /// Table "Organisations".
        /// </summary>
        public Table<Organisations> Organisations;

        /// <summary>
        /// Table "OrganisationAddresses".
        /// </summary>
        public Table<OrganisationAddresses> OrganisationsAddresses;

        /// <summary>
        /// Table "People".
        /// </summary>
        public Table<People> People;

        /// <summary>
        /// Table "PeopleAddresses".
        /// </summary>
        public Table<PersonAddresses> PeopleAddresses;

        /// <summary>
        /// Table "Tags".
        /// </summary>
        public Table<Tags> Tags;

        /// <summary>
        /// Table "Taxonomies".
        /// </summary>
        public Table<Taxonomies> Taxonomies;

        /// <summary>
        /// Table "Vocabs".
        /// </summary>
        public Table<Vocabs> Vocabs;

        #endregion

        #region Constructor

        /// <summary>
        /// Constructor.
        /// </summary>
        public OContext(string fileOrServerOrConnection) : base(fileOrServerOrConnection) { }

        #endregion

        #region Public methods

        /// <summary>
        /// Updates database on the server.
        /// </summary>
        public void UpdateDatabase()
        {
            SqlConnectionStringBuilder scsb = new SqlConnectionStringBuilder(Connection.ConnectionString);
            Server server = new Server(scsb.DataSource);
            string dbName = scsb.InitialCatalog;
            Database database = server.Databases[dbName];
            
            const string defaultSchemaName = "dbo";
            string tableName = String.Empty;
            string schemaName = String.Empty;
            string[] tableSections = null;
            Table table = null;
            Column column = null;

            MetaModel metaModel = new AttributeMappingSource().GetModel(this.GetType());
            foreach (MetaTable metaTable in metaModel.GetTables())
            {
                tableName = metaTable.TableName;
                schemaName = defaultSchemaName;
                tableSections = tableName.Split('.');
                if (tableSections.Length > 1)
                {
                    schemaName = tableSections[0];
                    tableName = tableSections[1];
                }

                table = database.Tables[tableName, schemaName];
                if (table == null)
                {
                    table = new Table(database, tableName, schemaName);
                    database.Tables.Add(table);

                    string mappedName = String.Empty;
                    PropertyInfo pi = null;

                    foreach (MetaType metaType in metaTable.RowType.InheritanceTypes)
                    {
                        foreach (MetaDataMember metaDataMember in metaType.DataMembers)
                        {
                            pi = (PropertyInfo) metaDataMember.Member;
                            if (pi == null) { continue; }
                            column = table.Columns[metaDataMember.MappedName];
                            if (column == null)
                            {
                                column = new Column(table, metaDataMember.MappedName);

                                //string dataType = metaDataMember.DbType;
                                //if (String.IsNullOrEmpty(dataType))
                                //{
                                    MemberInfo memberInfo = typeof(DataType).GetMember("Int", BindingFlags.IgnoreCase | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public).FirstOrDefault();
                                //}
                                column.DataType = (DataType) ((PropertyInfo) memberInfo).GetValue(null, null);

                                column.Nullable = false;
                                column.Identity = metaDataMember.IsDbGenerated && metaDataMember.IsPrimaryKey;
                                table.Columns.Add(column);
                            }
                        }
                    }

                    table.Create();
                }
            }
        }

        #endregion
    }
	
    /// <summary>
    /// Contains extension methods related to LINQ.
    /// </summary>
    public static class ContextExtension
    {
        /// <summary>
        /// Clear DataContext cache.
        /// </summary>
        /// <param name="context">context to clear</param>
        /// <remarks>refer to http://blog.robustsoftware.co.uk/2008/11/clearing-cache-of-linq-to-sql.html</remarks>
        public static void ClearCache(this DataContext context)
        {
            const BindingFlags FLAGS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
 
            var method = context.GetType().GetMethod("ClearCache", FLAGS);
            method.Invoke(context, null);
        }

        /// <summary>
        /// Build and execute a delete command using DataContext.ExecuteCommand(). Currently only support table with one column as the primary key.
        /// </summary>
        /// <param name="table">the table where the record exists</param>
        /// <param name="value">value of the primary key</param>
        /// <returns>number of affected rows</returns>
        public static int DeleteByPrimaryKey(this ITable table, object value)
        {
            table.ThrowIfNull<ArgumentNullException>("table");

            string tableName  = String.Empty;
            string columnName = String.Empty;
            Type entityType   = table.ElementType;

            // Get table name
            object[] attributes = entityType.GetCustomAttributes(typeof(TableAttribute), true);
            tableName = (attributes[0] as TableAttribute).Name;
            if (String.IsNullOrEmpty(tableName))
            {
                tableName = entityType.Name;
            }

            foreach (PropertyInfo pi in entityType.GetProperties())
            {
                object[] columnAttributes = pi.GetCustomAttributes(typeof(ColumnAttribute), true);
                if (columnAttributes.Length == 0)
                {
                    continue;
                }
                ColumnAttribute columnAttribute = columnAttributes[0] as ColumnAttribute;
                if (columnAttribute.IsPrimaryKey)
                {
                    columnName = columnAttribute.Name;
                    if (String.IsNullOrEmpty(columnName))
                    {
                        columnName = pi.Name;
                    }
                }
            }

            string query = String.Format("DELETE FROM [{0}] WHERE [{1}] = ", tableName, columnName);
            return table.Context.ExecuteCommand(query + "{0}", value);
        }
    }
}
